package com.uvnos4j.matilda.schedule;

import com.uvnos4j.matilda.schedule.domain.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 动态任务管理器
 *
 * @author Guo.wl
 */
@Slf4j
@Component
public class DynamicTaskManager {

    /**
     * 注入定时任务线程池
     */
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    /**
     * 注入所有定时任务存放Map
     */
    @Autowired
    @Qualifier(value = "scheduledTaskJobMap")
    private Map<String, ScheduleTaskJob> scheduledTaskJobMap;

    /**
     * 声明存放已经启动的任务map
     */
    private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();

    /**
     * 可重入锁
     */
    private ReentrantLock lock = new ReentrantLock();

    /**
     * 开始任务
     *
     * @param scheduleTask
     * @return
     */
    public boolean startTask(ScheduleTask scheduleTask) {
        // 校验任务是否存在
        if (!scheduledTaskJobMap.containsKey(scheduleTask.getTaskKey())) {
            return false;
        }
        return doStartTask(scheduleTask);
    }

    /**
     * 结束任务
     *
     * @param scheduleTask
     * @return
     */
    public Boolean stopTask(ScheduleTask scheduleTask) {
        String taskKey = scheduleTask.getTaskKey();
        // 校验任务实例是否是启动状态
        if (!scheduledFutureMap.containsKey(taskKey)) {
            return false;
        }
        log.info(">>>>>> 进入停止任务 {}  >>>>>>", taskKey);
        // 获取任务实例
        ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
        // 关闭实例
        scheduledFuture.cancel(true);
        scheduledFutureMap.remove(taskKey);
        log.info(">>>>>> 结束停止任务 {}  >>>>>>", taskKey);
        return true;

    }

    /**
     * 重启任务
     *
     * @param scheduleTask
     * @return
     */
    public Boolean restartTask(ScheduleTask scheduleTask) {
        String taskKey = scheduleTask.getTaskKey();
        log.info(">>>>>> 进入重启任务 {}  >>>>>>", taskKey);
        // 先停止
        stopTask(scheduleTask);
        // 再启动
        return startTask(scheduleTask);
    }

    /**
     * 执行启动任务
     */
    private boolean doStartTask(ScheduleTask scheduleTask) {
        String taskKey = scheduleTask.getTaskKey();
        String taskCron = scheduleTask.getTaskCron();
        log.info(">>>>>> 任务 [ {} ] ,cron={}", scheduleTask.getTaskDesc(), taskCron);

        log.info(">>>>>> 启动任务 {} 开始 >>>>>>", taskKey);
        log.info(">>>>>> 添加任务启动锁完毕");
        // 添加锁放一个线程启动，防止多人启动多次
        lock.lock();
        try {
            // 校验是否已经启动
            if (isStart(taskKey)) {
                log.info(">>>>>> 当前任务已经启动，无需重复启动！");
                return false;
            }
            // 获取需要定时调度的接口
            ScheduleTaskJob scheduledTaskJob = scheduledTaskJobMap.get(taskKey);
            ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob, new Trigger() {
                @Override
                public Date nextExecutionTime(TriggerContext triggerContext) {
                    CronTrigger cronTrigger = new CronTrigger(taskCron);
                    return cronTrigger.nextExecutionTime(triggerContext);
                }
            });
            // 将启动的任务放入 map
            scheduledFutureMap.put(taskKey, scheduledFuture);
            log.info(">>>>>> 启动任务 {} 结束 >>>>>>", taskKey);
            return true;
        } finally {
            // 释放锁
            lock.unlock();
            log.info(">>>>>> 释放任务启动锁完毕");
        }
    }

    /**
     * 任务是否已经启动
     */
    private Boolean isStart(String taskKey) {
        if (scheduledFutureMap.containsKey(taskKey)) {
            if (!scheduledFutureMap.get(taskKey).isCancelled()) {
                return true;
            }
        }
        return false;
    }

}